Skip to content

release-0.7: switch C++ toolchain to gcc-14#321

Merged
vporoshok merged 8 commits into
release-0.7from
chore/release-0.7-gcc-14
May 13, 2026
Merged

release-0.7: switch C++ toolchain to gcc-14#321
vporoshok merged 8 commits into
release-0.7from
chore/release-0.7-gcc-14

Conversation

@vporoshok
Copy link
Copy Markdown
Collaborator

Summary

The shared CI image ${DEV_REGISTRY}/prompp/ci-gcc-image:gcc-tools-${arch} is built only from the pp branch (see .github/workflows/build-ci-image.yaml) and now ships gcc-14 (debian-trixie). On release-0.7 the Bazel build still registered the g++-13 toolchain, whose builtin_include_directories pointed at /usr/lib/gcc/{x86_64,aarch64}-linux-gnu/13 — these no longer exist in the image. Linker flags also pulled libstdc++_libbacktrace.a, which gcc-14 has folded into libstdc++exp.a.

This brings release-0.7 in line with the new CI image by cherry-picking the toolchain-relevant pieces from pp:

  • b4f95e7af — full cherry-pick of e207bf7bc (PR Fix Bazel c++ toolchain #282 "fix toolchain"): replace -l:libstdc++.a with -lstdc++ (the explicit static-name flag is redundant under -static-libstdc++ and breaks on libstdc++-14 in some setups).
  • c29408fab — partial cherry-pick of 3df13958d (PR Gcc 14 clang tidy 21 #271 "Gcc 14 clang tidy 21"), restricted to the gcc-14 toolchain switch:
    • pp/MODULE.bazel: register cc_toolchain_for_g++-14_toolchain_{aarch64,x86_64} instead of the g++-13 ones.
    • pp/bazel/toolchain/BUILD: drop the now-unused g++-13 cc_toolchain_suite / cc_toolchain / toolchain / cc_toolchain_config entries (the g++-14 definitions were already present on this branch).
    • pp/bazel/toolchain/cc_toolchain_config.bzl: replace -lstdc++_libbacktrace with -lstdc++exp.
    • pp/scripts/test_coredump.sh: --config=g++-13--config=g++-14.

What is intentionally NOT picked from PR #271

PR #271 in pp is layered on top of PR #270 "GOST compile flags", which was never back-ported to release-0.7. The following parts of #271 are therefore left out:

  • Dockerfile.ci, all .github/workflows/* and the new cleanup_pr_images.yamlrelease-0.7 consumes the CI image built from pp, so no workflow changes are needed.
  • pp/.clang-tidy and the bulk of test/source updates that come with the clang-tidy 19 → 21 upgrade and the bugprone-* diagnostics — those depend on the GOST flags and stricter warnings introduced by GOST compile flags #270.
  • The gost_flags / clang_tidy plumbing in cc_toolchain_config.bzl and BUILD — same reason.

The 3-way merge on pp/bazel/toolchain/BUILD correctly dropped the clang_tidy = \":clang_tidy\" attribute that #271 added, because the cc_toolchain_config rule on release-0.7 does not declare it.

Test plan

  • cpp-tests workflow (matrix: OPT={dbg,opt}, SANITIZERS={no_sanitizers,with_sanitizers}, arch={amd64,arm64}) — confirms the C++ unit tests build and pass under gcc-14 with the new toolchain config.
  • build-go-bindings workflow (matrix: COMPILATION_MODE={dbg,opt}, ASAN={false,true}, arch={amd64,arm64}) — confirms cgo links cleanly with -lstdc++exp and without -l:libstdc++.a.
  • go-tests workflow — confirms downstream Go tests still pass with the relinked C++ artifact.
  • build_dev job (triggered automatically on PR) — confirms werf cpp-artifact (which uses the same toolkit image) still builds.

If any step fails with new -Werror warnings or a missing symbol that traces back to gost_flags / libstdc++exp, that is the signal to either back-port the relevant slice of #270 or revert a specific flag here.

Made with Cursor

vporoshok and others added 7 commits May 7, 2026 17:40
Cherry-picked from pp branch (e207bf7).

Required for the migration to gcc-14: -static-libstdc++ already drives
static linking of libstdc++, so the explicit -l:libstdc++.a is redundant
and breaks on debian-trixie / libstdc++-14 in some configurations.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cherry-picked from PR #271 on the pp branch (3df1395,
"Gcc 14 clang tidy 21"), restricted to the toolchain registration parts.

The shared CI image (registry/prompp/ci-gcc-image:gcc-tools-${arch}) is built
only from the pp branch and now ships gcc-14 (debian-trixie). The release-0.7
build was still registering the g++-13 Bazel toolchain, whose
builtin_include_directories pointed at /usr/lib/gcc/{x86_64,aarch64}-linux-gnu/13
which no longer exist in the image; on top of that the linker was pulling
libstdc++_libbacktrace, which gcc-14 has folded into libstdc++exp.

This commit:
- registers cc_toolchain_for_g++-14_toolchain_{aarch64,x86_64} in
  pp/MODULE.bazel,
- drops the now-unused g++-13 cc_toolchain_suite / cc_toolchain / toolchain /
  cc_toolchain_config entries in pp/bazel/toolchain/BUILD,
- replaces -lstdc++_libbacktrace with -lstdc++exp in the default linker flags
  in pp/bazel/toolchain/cc_toolchain_config.bzl.

The clang-tidy / GOST-flag changes from PR #271 (which depend on PR #270
"GOST compile flags") are intentionally excluded because they were never
back-ported to release-0.7 and would pull in additional warnings that the
release branch has not been audited against.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cherry-picked from PR #271 on the pp branch (file pp/third_party/patches/
quasis_crypto/0001-md5.hh.patch from 3df1395).

gcc-14 rejects the use of the built-in __is_trivially_copyable trait inside
a requires-clause ("error: use of built-in trait '__is_trivially_copyable'
in function signature; use library traits instead"). The updated patch:

- replaces __is_trivially_copyable(T) with std::is_trivially_copyable_v<T>
  in the requires-clauses of MD5::update overloads,
- adds the missing #pragma once and <bit>/<type_traits> includes,
- silences a -Wmaybe-uninitialized false positive around hasher.update().

Required to make pp/prometheus tests build under gcc-14.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cherry-picked from PR #287 on the pp branch (c80d137)
for the production-code parts only.

GCC 14's libstdc++ uses sized-range fast paths in std::ranges::equal, which
breaks BareBones::GenericBitset because GenericBitset::size() returns the
allocated bit width, not ranges::distance(begin, end) (the set-bit count).
This caused runtime failures in pp/bare_bones/tests/bitset_tests.cpp
(BitsetFixture.random_access, BitsetConstructorsFixture.MoveConstructor /
MoveAssignment / MoveAssignmentNonEmpty) and in
pp/series_data/tests/querier/{instant_querier,querier}_tests.cpp where the
querier returns a Bitset that is then compared against an initializer_list.

Specialise std::ranges::disable_sized_range<GenericBitset<R>> = true to
restore element-wise comparison.

Also update pp/go/cppbridge/entrypoint.go cgo LDFLAGS:
  -lstdc++_libbacktrace      -> -lstdc++exp
  -l:libstdc++_libbacktrace.a -> -l:libstdc++exp.a
GCC 14 has folded libstdc++_libbacktrace into libstdc++exp; without this the
go-tests / build-go-bindings step fails to link.

Co-authored-by: Cursor <cursoragent@cursor.com>
set_const_labels was sorting label pointers with
  return a->name < b->name;
where a->name and b->name are const String*. This compares stack
addresses, not strings, so the resulting iteration order — and therefore
MetricDescriptor::id / dim_hash — depended on whatever order the local
String objects ended up at on the stack.

Under gcc-13 (and under gcc-14 on the pp branch with -fstack-protector-
strong from PR #270 GOST flags) the layout happened to match the
alphabetical order, so primitives_test/MetricDescriptorFixture.TestWithLabels
produced the expected 9433770049495071547 / 1413792954011449091. On
release-0.7 under plain gcc-14 the layout reverses, the labels are
hashed in the opposite order and the test fails with
8942707084255367343 / 18042764105132557107.

Compare the dereferenced strings instead. This matches what the Go
prometheus/client_golang side does (sort labels lexicographically by
name) and makes id/dim_hash deterministic across compilers / build
flags. The expected values in the test correspond to this alphabetical
order, so the test goes green.

MetricDescriptor::id is computed in the constructor and not persisted,
so the only effect of this change at runtime is that two metrics with
the same labels but constructed in different process invocations now
get the same id (which they already did on pp, just not on release-0.7
under the new compiler).

Co-authored-by: Cursor <cursoragent@cursor.com>
PR #317 (Jemalloc arena purge) added pp/metrics/jemalloc_metrics.h that
unconditionally references BareBones::jemalloc::FreeArenas::*, but the
FreeArenas struct itself is declared inside `#if JEMALLOC_AVAILABLE` in
bare_bones/jemalloc.h. JEMALLOC_AVAILABLE is set by preprocess.h only
when __has_include(<jemalloc/jemalloc.h>) succeeds.

pp/BUILD excludes @jemalloc from the entrypoint deps under
--config=asan:

    deps = [...] + select({
        "//bazel/toolchain:with_asan": [],
        "//conditions:default": ["@jemalloc"],
    })

so the asan build-entrypoint legs of build-go-bindings (matrix
ASAN=true) fail with "'BareBones::jemalloc::FreeArenas' has not been
declared" while compiling entrypoint/metrics.cpp.

Wrap the JemallocMetrics struct and its CreateMetricsPage registration
in `#if JEMALLOC_AVAILABLE` so the asan variant simply skips registering
arena-pool metrics (which have no source of truth in that build anyway,
since FreeArenas itself does not exist there).

The non-asan path is unaffected: @jemalloc is still a dep,
<jemalloc/jemalloc.h> is on the include path, JEMALLOC_AVAILABLE stays
1, JemallocMetrics is declared and registered as before.

Verified locally in the arm devcontainer: \`make compilation_mode=dbg
asan=true build-entrypoint\` now succeeds.

Co-authored-by: Cursor <cursoragent@cursor.com>
After PR #317 (Jemalloc arena purge), prompp_metrics_register() — invoked
from cppbridge.init() — globally registers a JemallocMetrics page with
three counters/gauges:

  prompp_common_jemalloc_arena_pool_releases_total
  prompp_common_jemalloc_arena_pool_released_bytes_total
  prompp_common_jemalloc_arena_pool_released_bytes_max

Iterating CppMetrics now always yields those three on top of whatever
test pages the suite creates, so TestCppMetricsSuite/{TestNoMetricPages,
TestOneMetricsPage,TestTwoMetricPages} fail with "should have N item(s),
but has N+3" on builds that include @jemalloc (i.e. the asan=false legs
of build-go-bindings → go-tests on PR #321). The asan=true builds were
already fine because the previous commit gates JemallocMetrics on
JEMALLOC_AVAILABLE.

Filter the well-known prompp_common_jemalloc_ prefix out of the suite's
getMetrics() helper so the assertions stay scoped to the per-test pages
the suite actually owns. The filter is a no-op when JemallocMetrics is
not registered (asan builds, and any future merge into pp where the page
does not exist yet), so behaviour does not change there.

Verified locally in the arm devcontainer:

  go test -count=1 -run TestCppMetricsSuite ./pp/go/cppbridge

passes all three subtests.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vporoshok vporoshok requested a review from cherep58 May 12, 2026 12:58
The werf build_dev pipeline (toolkit / cpp-artifact / prompp-artifact) uses
Dockerfile.ci to build its base image. On this branch it was still pinned to
ubuntu:24.04 + the unversioned `gcc g++` packages, which on noble resolve to
gcc-13 / libstdc++-13-dev.

After Switch C++ toolchain to gcc-14 (c29408f) the Bazel toolchain in
pp/bazel/toolchain points at /usr/lib/gcc/{x86_64,aarch64}-linux-gnu/14 and
the prefixed cppbridge .a archives (pulled from the shared bazel remote
cache, populated from the pp branch's gcc-14 CI image) reference
std::stacktrace symbols that only live in libstdc++exp.a from
libstdc++-14-dev. With gcc-13 in the toolkit image, prompp-artifact's
beforeSetup link step (`promu build` → cgo `-extldflags '-static'`) blows
up with:

    /src/pp/go/cppbridge/amd64_*_entrypoint_aio_prefixed_opt.a(...): in
    function `std::stacktrace_entry::_M_get_info(...)':
    /usr/include/c++/14/stacktrace:165: undefined reference to
    `std::stacktrace_entry::_Info::_M_populate(unsigned long)'
    /usr/include/c++/14/stacktrace:221: undefined reference to
    `std::__stacktrace_impl::_S_current(int (*)(void*, unsigned long), void*, int)'

That is exactly the link surface that PR #287 already aligned on the pp
branch by moving Dockerfile.ci to debian:trixie-slim and installing gcc-14
+ g++-14 + libstdc++-14-dev + liblzma-dev + LLVM 21. Take that file
verbatim from origin/pp HEAD (9e06a6a) so build_dev sees the same
toolchain the cppbridge archives were compiled against. As a side effect
this also bumps the in-image Go to 1.26.2 (which matches the build-go-
bindings / go-tests legs that already use the shared gcc-14 image) and
pulls in binutils-gold for the arm64 cgo+PIE workaround Go 1.26.2 still
requires.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vporoshok vporoshok merged commit 9d3321c into release-0.7 May 13, 2026
27 of 29 checks passed
@vporoshok vporoshok deleted the chore/release-0.7-gcc-14 branch May 13, 2026 07:07
vporoshok added a commit that referenced this pull request May 15, 2026
Brings the v0.7.11 release-0.7 fixes/enhancements into pp:
- Jemalloc arena pool recycling (#317) with prompp_common_jemalloc_arena_pool_* metrics
- GCC 14 C++ toolchain switch (#321)
- Match aarch64 jemalloc lg-page already in pp; release-0.7 carries the same fix
- Bump Go to 1.26.3 and golang.org/x/net to v0.53.0

Conflict resolution notes:
- CHANGELOG.md: keep v0.8.0 entry; insert v0.7.11 between v0.8.0 and v0.7.10.
- VERSION: keep 0.8.0-rc2 from pp.
- Dockerfile.ci / werf.yaml: take 1.26.3 Go from release-0.7.
- go.mod: keep highest version of each golang.org/x dep (oauth2/time from pp,
  net/sys/text/tools/crypto/mod/term/telemetry from release-0.7); go mod tidy
  applied to refresh go.sum.
- pp/MODULE.bazel: keep HEAD module() block; gcc-14 toolchains were already
  registered in HEAD below.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants